﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Xna.Framework.Graphics;
using TinyEngine.Core.Services;
using Microsoft.Xna.Framework;
using TinyEngine.Core.Renderer;
using System.IO;
using TinyEngine.Core.Entities.Parsers;

namespace TinyEngine.Core.Entities
{
    [TinyEngine.Core.Attributes.EntityParser(typeof(TileGridParser))]
    public class TileGridEntity : TinyEngine.Core.Entities.NullEntity, IRenderable, IDebugRenderable
    {
        public string FileRef { get; set; }

        private Dictionary<string, Asset.SpriteAsset> assetList = new Dictionary<string, Asset.SpriteAsset>();
        public bool Visible { get; set; }
        public Dictionary<string, Asset.SpriteAsset> AssetList
        {
            get { return assetList; }
        }

        public TileGridEntity() 
        {
            Tiles = new List<Tile>();
        }

        public int TilesX { get; set; }
        public int TilesY { get; set; }

        public int TileSizeX { get; set; }
        public int TileSizeY { get; set; }

        public List<Tile> Tiles { get; set; }

        private int GetIndex(int x, int y, int cols)
        {
            return y * cols + x;
        }
        
        public Rectangle GetTilePosition(int x, int y)
        {
            var rect = new Rectangle(x * this.TileSizeX,
                                 y * this.TileSizeY,
                                 this.TileSizeX,
                                 this.TileSizeY);
            rect.Offset(new Point((int)this.Position.X, (int)this.Position.Y));
            rect.Offset(new Point((int)-this.Origin.X, (int)-this.Origin.Y));

            var index = GetTileIndex(x, y, this.TilesX);
            
            return rect;
        }

        public void Draw(float elapsed)
        {
            var renderer = Services.ServiceManager.GetService<SpriteBatchService>();

            for (int y = 0; y < TilesY; y++)
            {
                for (int x = 0; x < TilesX; x++)
                {
                    
                    int index = GetIndex(x, y, TilesX);
                    var tilePos = GetTilePosition(x, y);
                    
                    var tile = this.Tiles[index];
                    if (tile.ImageIndex == -1)
                        continue;

                    var tileSource = GetTileSourceRectangle(tile.ImageIndex);
                    tilePos.Offset(tile.OffSet.ToPoint());
                    if (tile.OffSet != Vector2.Zero)
                        Console.Write("");
                    var texture = assetList.First().Value.Asset;
                    renderer.Batch.Draw(texture, tilePos, tileSource, Color.White);

                }
            }
        }

        public int MaxWidth
        {
            get { return this.TilesX * this.TileSizeX; }
        }
        public int MaxHeight
        {
            get { return this.TilesY * this.TileSizeY; }
        }
        public override void PositionChanged()
        {
            this.SetBounds(new Rectangle(
                (int)(this.Position.X - this.Origin.X),
                (int)(this.Position.Y - this.Origin.Y),
                MaxWidth,
                MaxHeight));

            base.PositionChanged();
        }

        Texture2D texture
        {
            get
            {
                return assetList.First().Value.Asset;
            }
        }
        int TextureCols
        {
            get { return this.texture.Width / this.TileSizeX; }
        }

        public static int GetTileIndex(int x, int y, int cols)
        {
            return y * cols + x;
        }

        public static Rectangle GetTileSourceRectangle(int index, int cols, int tileSizeX, int tileSizeY)
        {
            if (index == -1)
                return Rectangle.Empty;

            int tileIndex = index;

            Point tilePos = new Point();
            tilePos.X = (int)Math.Floor(tileIndex / (double)cols);
            tilePos.Y = (int)(tileIndex % cols);
            Point tilePosOnTexture = new Point();
            tilePosOnTexture.Y = tileSizeY * tilePos.X;
            tilePosOnTexture.X = tileSizeX * tilePos.Y;
            return new Rectangle(tilePosOnTexture.X, tilePosOnTexture.Y, tileSizeX, tileSizeY);
        }
        public Rectangle GetTileSourceRectangle(int index)
        {
             if(index == -1)
                return Rectangle.Empty;

            int tileIndex = index;

            Point tilePos = new Point();
            tilePos.X = (int)Math.Floor(tileIndex / (double)this.TextureCols);
            tilePos.Y = (int)(tileIndex % this.TextureCols);
            Point tilePosOnTexture = new Point();
            tilePosOnTexture.Y = (int)TileSizeY * tilePos.X;
            tilePosOnTexture.X = (int)TileSizeX * tilePos.Y;
            return new Rectangle(tilePosOnTexture.X, tilePosOnTexture.Y, TileSizeX, TileSizeY);
        }

        public Rectangle GetTileSourceRectangle(int x, int y)
        {
            var rect = new Rectangle(
                         x * TileSizeX,
                         y * TileSizeY,
                         TileSizeX,
                         TileSizeY);
            return rect;
        }

        public override void Register(Scene.TinyScene owner)
        {
            base.Register(owner);

            this.PositionChanged();
        }

        public Vector2 Origin { get; set; }

        public void DrawDebug(float elapsed)
        {
            var renderer = Services.ServiceManager.GetService<PrimitiveBatchService>();

            //Draw full bounds
            renderer.Batch.DrawBox(this.BoundingRectangle);


            for (int y = 0; y < TilesY; y++)
            {
                for (int x = 0; x < TilesX; x++)
                {

                    int index = GetIndex(x, y, TilesX);
                    var tilePos = GetTilePosition(x, y);

                    var tile = this.Tiles[index];
                    if (tile.ImageIndex == -1)
                        continue;

                    renderer.Batch.DrawBox(tilePos);
                    tilePos.Inflate(-6, -6);
                    renderer.Batch.DrawBox(tilePos);

                }
            }
        }

        public List<Tile> GetTilesInBounds(Rectangle rectangle)
        {
            List<Tile> tiles = new List<Tile>();
            rectangle.Offset(this.Position.ToPoint());
            rectangle.Offset(this.Origin.ToPoint());

            var bounds = rectangle;
            
            int leftTile = (int)Math.Floor((float)bounds.Left / this.TileSizeX);
            int rightTile = (int)Math.Ceiling((float)bounds.Right / this.TileSizeX) - 1;
            int topTile = (int)Math.Floor((float)bounds.Top / this.TileSizeY);
            int bottomTile = (int)Math.Ceiling((float)bounds.Bottom / this.TileSizeY) -1;


            for (int y = topTile; y <= bottomTile; y++)
            {
                for (int x = leftTile; x <= rightTile; x++)
                {
                    var index = GetTileIndex(x, y, this.TilesX);
                    if (index > 0 && index < this.Tiles.Count)
                    {
                        tiles.Add(this.Tiles[index]);
                    }
                }
            }
            return tiles;
        }
    }

    public class Tile
    {
        public int IndexX { get; set; }
        public int IndexY { get; set; }
        public int ImageIndex { get; set; }
        public Vector2 OffSet { get; set; }

    }
}
